<?php

//require 'paypal/PayPalNVPService.php';

/**
 * Provides a wrapper for Paypal Direct Payment API.
 * 
 */
class PaypalNvpComponent {
	const PAYPAL_ORDER_TYPE_SALE = 'Sale';
	const PAYPAL_ACK_SUCCESS = 'Success';
	const PAYPAL_ACK_SUCCESS_WITH_WARNING = 'SuccessWithWarning';
	
	const PAYPAL_RECURRING_PAYMENT_PROFILE_STATUS_ACTIVE = 'ActiveProfile';
	const PAYPAL_RECURRING_PAYMENT_PROFILE_STATUS_PENDING = 'PendingProfile';
	const PAYPAL_RECURRING_PAYMENT_PROFILE_STATUS_CANCELLED = 'CancelledProfile';
	
	const IPN_PROFILE_CANCELLED = 'Cancelled';
	const IPN_PAYMENT_COMPLETED = 'Completed';
	const IPN_PAYMENT_FAILED = 'Failed';
	const IPN_OTHERS = 'Others';
	
	private $_paypal;
	private $_ipnData = array();
	private $_logFile = 'PaypalIPN';
	
	/**
     * Called before the Controller::beforeFilter().
     *
     * @param object $controller Controller with components to initialize
     */
    function __construct() {
        $this->_paypal = new PayPalNVPService();
    }
	
	public function getExpressCheckoutDetails($order) {
		$token		=	urlencode($order['token']);		
		
		$nvpstr = "&TOKEN=$token";
		
		return $this->_paypal->hash_call("GetExpressCheckoutDetails", $nvpstr);
		
	}
	
	public function doExpressCheckoutPayment($order) {
		$token		=	urlencode($order['token']);
		$action		=	urlencode($order['action']);
		$payerId	=	urlencode($order['payerId']);
		$amount 	=	urlencode($order['amount']);
		$currencyID					=	urlencode($order['currencyID']);
		
		$nvpstr = "&TOKEN=$token&PAYERID=$payerId&PAYMENTACTION=$action&AMT=$amount&CURRENCYCODE=$currencyID";
		
		return $this->_paypal->hash_call("DoExpressCheckoutPayment", $nvpstr);
		
	}
	
	public function createRecurringPaymentsProfile($order, $orderTrial = array()) {
		$ccType 			=	urlencode($order['type']);
		$ccNumber 			=	urlencode($order['number']);
		$cvv2Number 		=	urlencode($order['cvv2']);
		$expDate 			=	urlencode($order['expiration']);
		$firstName 			=	urlencode($order['firstName']);
		$lastName 			=	urlencode($order['lastName']);
		$profileStartDate	=	urlencode($order['profileStartDate']);
		$billingPeriod		=	urlencode($order['billingPeriod']);
		$billingFrequency	=	urlencode($order['billingFrequency']);
		$amount 			=	urlencode($order['amount']);
		$desc 				= 	urlencode($order['desc']);
		
		$nvpstr	=  	"&CREDITCARDTYPE=$ccType&ACCT=$ccNumber&CVV2=$cvv2Number&EXPDATE=$expDate&FIRSTNAME=$firstName&LASTNAME=$lastName&DESC=$desc" . 
					"&PROFILESTARTDATE=$profileStartDate&BILLINGPERIOD=$billingPeriod&BILLINGFREQUENCY=$billingFrequency&AMT=$amount";

		// Trial
		if(!empty($orderTrial)) {
			$trialBillingPeriod			=	urlencode($orderTrial['billingPeriod']);
			$trialBillingFrequency		=	urlencode($orderTrial['billingFrequency']);
			$trialTotalBillingCycles	=	urlencode($orderTrial['totalBillingCycles']);
			$trialAmount 				=	urlencode($orderTrial['amount']);
			
			$nvpstr .= "&TRIALBILLINGPERIOD=$trialBillingPeriod&TRIALBILLINGFREQUENCY=$trialBillingFrequency&TRIALTOTALBILLINGCYCLES=$trialTotalBillingCycles&TRIALAMT=$trialAmount";
		}
		
		return $this->_paypal->hash_call('CreateRecurringPaymentsProfile', $nvpstr);
	}
    
	public function changeCreditCard($profileId, $cc) {
		$ccType 			=	urlencode($cc['type']);
		$ccNumber 			=	urlencode($cc['number']);
		$cvv2Number 		=	urlencode($cc['cvv2']);
		$expDate 			=	urlencode($cc['expiration']);
		$firstName 			=	urlencode($cc['firstName']);
		$lastName 			=	urlencode($cc['lastName']);
		
		$nvpstr	=  	"&PROFILEID=$profileId&CREDITCARDTYPE=$ccType&ACCT=$ccNumber&CVV2=$cvv2Number&EXPDATE=$expDate&FIRSTNAME=$firstName&LASTNAME=$lastName";

		return $this->_paypal->hash_call('UpdateRecurringPaymentsProfile', $nvpstr);
	}
    
	public function cancelRecurringPaymentProfile($profileId, $note) {
		$action = 'Cancel';
		$nvpstr = "&PROFILEID=$profileId&ACTION=$action&NOTE=$note";
		
		return $this->_paypal->hash_call('ManageRecurringPaymentsProfileStatus', $nvpstr);
	}
    
	/**
	 * SetExpressCheckout for recurring payments
	 * @param $order
	 * @return unknown_type
	 */
	public function setExpressCheckout($order) {
		$action		 				=	urlencode($order['action']);
		$amount 					=	urlencode($order['amount']);
		$returnUrl					=	urlencode($order['returnUrl']);
		$cancelUrl					=	urlencode($order['cancelUrl']);
		$l_billingAgreementDesc0	= 	urlencode($order['desc']);
		$l_billingType0 			= 	'RecurringPayments';
		$currencyID					=	urlencode($order['currencyID']);
		
		$nvpstr	= "&AMT=$amount&PAYMENTACTION=$action&RETURNURL=$returnUrl&CANCELURL=$cancelUrl&L_BILLINGTYPE0=$l_billingType0&L_BILLINGAGREEMENTDESCRIPTION0=$l_billingAgreementDesc0&CURRENCYCODE=$currencyID";
   		
		return $this->_paypal->hash_call('SetExpressCheckout', $nvpstr);
		
	}
    
	public function createRecurringPaymentsProfileWithExpressCheckout($order, $orderTrial = array()) {
		$token				=	urlencode($order['token']);
		$profileStartDate	=	urlencode($order['profileStartDate']);
		$billingPeriod		=	urlencode($order['billingPeriod']);
		$billingFrequency	=	urlencode($order['billingFrequency']);
		$amount 			=	urlencode($order['amount']);
		$desc 				= 	urlencode($order['desc']);
		
		$nvpstr	= "&TOKEN=$token&DESC=$desc&PROFILESTARTDATE=$profileStartDate&BILLINGPERIOD=$billingPeriod&BILLINGFREQUENCY=$billingFrequency&AMT=$amount";

		// Trial
		if( !empty($orderTrial) ) {
			$trialBillingPeriod			=	urlencode($orderTrial['billingPeriod']);
			$trialBillingFrequency		=	urlencode($orderTrial['billingFrequency']);
			$trialTotalBillingCycles	=	urlencode($orderTrial['totalBillingCycles']);
			$trialAmount 				=	urlencode($orderTrial['amount']);
			
			$nvpstr .= "&TRIALBILLINGPERIOD=$trialBillingPeriod&TRIALBILLINGFREQUENCY=$trialBillingFrequency&TRIALTOTALBILLINGCYCLES=$trialTotalBillingCycles&TRIALAMT=$trialAmount";
			
		}
		
		return $this->_paypal->hash_call('CreateRecurringPaymentsProfile', $nvpstr);
	}
    
	/**
	 * Validate the IPN notification
	 *
	 * @param none
	 * @return mixed data
	 * 		- false: Invalid IPN
	 * 		- string:
	 *			. Cancelled: 	Profile canceled from paypal.com interface
	 * 			. Completed: 	Payment succeeded
	 * 			. Failed: 		Payment failed	
	 * 			. Others: Dont care
	 */
	public function validateIpn($data) {
		$urlParsed = parse_url($this->_paypal->getIPNEndPoint());
		$postString = 'cmd=_notify-validate';

		foreach ($data as $field=>$value) {
			$this->_ipnData["$field"] = $value;
			$postString .= "&$field=" . urlencode(stripslashes($value));
		}
		 
		$ipnResponse = '';
		
		// open the connection to paypal
		$fp = fsockopen($urlParsed['host'], '80', $errNum, $errStr, 30);

		if(!$fp) {
			// Could not open the connection, log error if enabled
			return false;
			
		} else {
			// Post the data back to paypal
			fputs($fp, "POST ". $urlParsed['path'] ." HTTP/1.1\r\n");
			fputs($fp, "Host: ". $urlParsed['host'] ."\r\n");
			fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
			fputs($fp, "Content-length: " . strlen($postString) . "\r\n");
			fputs($fp, "Connection: close\r\n\r\n");
			fputs($fp, $postString . "\r\n\r\n");

			// loop through the response from the server and append to variable
			while(!feof($fp)) {
				$ipnResponse .= fgets($fp, 1024);
			}

		 	fclose($fp);
		}

		if (eregi('VERIFIED', $ipnResponse)) { // Valid IPN transaction.
		 	return $this->parseIPN();
		 	
		} else { // Invalid IPN transaction.  Check the log for details.
			$this->log("PaypalNvpComponent::validateIpn(): $ipnResponse" , $this->_logFile);
			return false;
		}
	}
	
	/* Possible Recurring Payments Instant Payment Notifications
	 * 	1. Profile successfully created: 							don't care
	 * 	2. Profile creation failed: 								don't care
	 * 	3. Profile canceled from paypal.com interface				CARE
	 * 	4. Initial payment either succeeded or failed: 				don't care - We don't have Init payment 
	 * 	5. Payment either succeeded or failed 						CARE
	 * 		(during either trial period or regular payment period)	
	 * 	6. Outstanding payment either succeeded or failed: 			don't care - We don't have Init payment
	 * 	7. Maximum number of failed payments reached: 				don't care - We don't include MAXFAILEDPAYMENTS in CreateRecurringPaymentsProfile
	 * 
	 * 	Need to be processed: 3, 5
	 * 	Note: 3 Not efftect to profile created using Direct Payment
	 * 
	 * @return string
	 *			. Cancelled: Case 3
	 * 			. Completed: Payment succeeded
	 * 			. Failed: Payment failed	
	 * 			. Others: Dont care
	 */
	private function parseIPN() {
		if($this->_ipnData['txn_type'] == 'recurring_payment_profile_cancel' && $this->_ipnData['profile_status'] == 'Cancelled') { // 3 - Profile canceled from paypal.com interface
			return self::IPN_PROFILE_CANCELLED;
		} 
		
		if($this->_ipnData['txt_type'] == 'recurring_payment') { // 5 - Payment either succeeded or failed
			if($this->_ipnData['payment_status'] == 'Completed') { // PAYMENT Succeed
				return self::IPN_PAYMENT_COMPLETED;
			} else { // PAYMENT FAILED
				return self::IPN_PAYMENT_FAILED;
			}
		}
		
		return self::IPN_OTHERS;
	}
	
	public function getIpnData() {
		return $this->_ipnData;
		
	}
	
	public function getPaypalUrl() {
		return PAYPAL_URL;
		
	}
	
	/**
	 * Get L_LONGMESSAGEi
	 * 
	 * @param $response
	 * @return array long error messages
	 */
	public function getLongErrorMsgs($response) {
		$count=0;
		$result = array();
		
		while (isset($response['L_LONGMESSAGE' . $count])) {	
			$result[] = $response['L_LONGMESSAGE' . $count];	
			$count++;  
		}
		
		return $result; 
		
	}
	public function getFirstErrorMsg($response) {
		if ( isset($response['L_LONGMESSAGE0']) ) {	
			return $response['L_LONGMESSAGE0'];   
		}
		
		return ''; 
	}
	
	public function isSuccess($result) {
		return 	(strtoupper($result['ACK']) === strtoupper(self::PAYPAL_ACK_SUCCESS)) ||
			   	(strtoupper($result['ACK']) === strtoupper(self::PAYPAL_ACK_SUCCESS_WITH_WARNING));
	}
	
}